package com.awen.htmlparser.parser;

import com.awen.htmlparser.Exception.ParseException;
import com.awen.htmlparser.rule.CyouHtmlParseRule;
import com.awen.htmlparser.rule.CyouHtmlParseRuleItem;
import com.awen.htmlparser.rule.RuleConfig;
import com.awen.htmlparser.util.PlainTextProcessUtil;
import com.awen.htmlparser.util.Result;
import org.apache.commons.beanutils.MethodUtils;
import org.htmlparser.Node;
import org.htmlparser.Parser;
import org.htmlparser.util.NodeList;
import org.htmlparser.util.ParserException;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.List;

/**
 * CyouHtml解析器
 * User: awen
 * Date: 13-4-8
 * Time: 上午9:55
 * To change this template use File | Settings | File Templates.
 */
public class CyouHtmlUrlParser/* extends AbstractCyouParser */ {


    /**
     * 根据解析规则解析指定url
     *
     * @param url
     * @param cyouHtmlParseRule
     * @return 返回解析结果 json 格式
     *         {success:true|false,
     *         ['errorCode':'',]
     *         ['msg' : '',]
     *         'data' : {
     *         key : value
     *         }
     *         }
     */
    public static Result parseHtml(String url, CyouHtmlParseRule cyouHtmlParseRule) {
        Result result = new Result();
        if (cyouHtmlParseRule.isNeedLocateHtml()) {//获取定位的htmlText
            String htmlText = null;
            try {
                htmlText = locateMainHtml(cyouHtmlParseRule, url);
            } catch (ParserException e) {
                result.setSuccess(false);
                result.setMsg("解析规则" + cyouHtmlParseRule.getName() + "中定位HTMLText时候出错,解析失败.\n失败原因：HTMLParser解析异常");
                return result;
            } catch (ParseException e) {
                result.setSuccess(false);
                result.setMsg("解析规则" + cyouHtmlParseRule.getName() + "中定位HTMLText时候出错,解析失败.\n失败原因：" + e.getMessage());
                return result;
            }
            return parseHtmlText(cyouHtmlParseRule.getRuleItems(), htmlText);
        } else {
            result.setData(new HashMap<String, String>());
            List<CyouHtmlParseRuleItem> ruleItems = cyouHtmlParseRule.getRuleItems();
            for (CyouHtmlParseRuleItem cyouHtmlParseRuleItem : ruleItems) {
                String key = cyouHtmlParseRuleItem.getKey();
                String value = null;
                Parser parser = new Parser();
                try {
                    parser.setURL(url);
                    parser.setEncoding(cyouHtmlParseRuleItem.getEncoding());

                } catch (ParserException e) {
                    result.setSuccess(false);
                    result.setMsg("解析规则" + cyouHtmlParseRule.getName() + "中字段" + cyouHtmlParseRuleItem.getItemName() + "时候出错,解析失败.\n失败原因：HTMLParser设置编码异常");
                    return result;
                }
                NodeList nodeList = null;
                try {
                    nodeList = parser.parse(cyouHtmlParseRuleItem.getRuleConfig().getNodeFilter());
                } catch (ParserException e) {
                    result.setSuccess(false);
                    result.setMsg("解析规则" + cyouHtmlParseRule.getName() + "中定位HTMLText时候出错,解析失败.\n失败原因：HTMLParser解析异常");
                    return result;
                }
                if (cyouHtmlParseRuleItem.getRuleConfig().getChild() != null) {
                    try {
                        value = parseItemHtml(cyouHtmlParseRuleItem, cyouHtmlParseRuleItem.getRuleConfig().getChild(), nodeList.elementAt(cyouHtmlParseRuleItem.getRuleConfig().getIndex()).toHtml());
                    } catch (ParserException e) {
                        result.setSuccess(false);
                        result.setMsg("解析规则" + cyouHtmlParseRule.getName() + "中字段" + cyouHtmlParseRuleItem.getItemName() + "时候出错,解析失败.\n失败原因：HTMLParser设置编码异常");
                    } catch (ParseException e) {
                        result.setSuccess(false);
                        result.setMsg("解析规则" + cyouHtmlParseRule.getName() + "中字段" + cyouHtmlParseRuleItem.getItemName() + "时候出错,解析失败.\n失败原因：" + e.getMessage());
                    }
                } else {
                    value = parsePlainText(
                            cyouHtmlParseRuleItem.getPlainTextFormat(),
                            nodeList.elementAt(cyouHtmlParseRuleItem.getRuleConfig().getIndex()).toPlainTextString().trim());
                }
                result.getData().put(key, value);
            }
            //打印日志
            result.setMsg("解析成功");
            return result;
        }
    }

    /**
     * 定位抓取页面主面板，ruleConfig仅一层
     * @param cyouHtmlParseRule
     * @param url
     * @return
     * @throws ParserException
     * @throws ParseException
     */
    private static String locateMainHtml(CyouHtmlParseRule cyouHtmlParseRule, String url) throws ParserException, ParseException {
        String mainHtml = null;
        Parser parser = new Parser();
        NodeList nodeList = null;
        parser.setEncoding(cyouHtmlParseRule.getEncoding());
        parser.setURL(url);
        nodeList = parser.parse(cyouHtmlParseRule.getLocateRuleConfig().getNodeFilter());
        if (nodeList == null || nodeList.size() == 0) {
            return "";
        }
        mainHtml = nodeList.elementAt(cyouHtmlParseRule.getLocateRuleConfig().getIndex()).toHtml();
        return mainHtml;
    }

    /**
     * @param ruleItems
     * @param htmlText
     * @return
     */
    public static Result parseHtmlText(List<CyouHtmlParseRuleItem> ruleItems, String htmlText) {
        Result result = new Result();
        result.setData(new HashMap<String, String>());
        for (CyouHtmlParseRuleItem cyouHtmlParseRuleItem : ruleItems) {
            String itemResult = null;
            try {
                itemResult = parseItemHtml(cyouHtmlParseRuleItem, htmlText);
            } catch (ParserException e) {
                result.setSuccess(false);
                result.setMsg("解析规则" + cyouHtmlParseRuleItem.getCyouHtmlParseRule().getName() + "中字段" + cyouHtmlParseRuleItem.getItemName() + "时候出错,解析失败.\n失败原因：HTMLParser解析异常");
                return result;
            } catch (ParseException e) {
                result.setSuccess(false);
                result.setMsg("解析规则" + cyouHtmlParseRuleItem.getCyouHtmlParseRule().getName() + "中字段" + cyouHtmlParseRuleItem.getItemName() + "时候出错,解析失败.\n失败原因：" + e.getMessage());
                return result;
            }
            result.getData().put(cyouHtmlParseRuleItem.getKey(), itemResult);
        }
        //打印日志
        result.setMsg("解析成功");
        return result;
    }

    /**
     * 对cyouHtmlParseRuleItem通过读取ruleConfig配置（nodeFilter，index等）对当前htmlText进行解析
     *
     * @param cyouHtmlParseRuleItem
     * @param htmlText
     * @return
     * @throws ParserException
     */
    protected static String parseItemHtml(CyouHtmlParseRuleItem cyouHtmlParseRuleItem, String htmlText) throws ParserException, ParseException {
        if(cyouHtmlParseRuleItem.getPlainTextFormat()==null)System.out.println(cyouHtmlParseRuleItem);
        return parseItemHtml(cyouHtmlParseRuleItem, cyouHtmlParseRuleItem.getRuleConfig(), htmlText);

    }

    /**
     * 对cyouHtmlParseRuleItem通过读取ruleConfig配置（nodeFilter，index等）对当前htmlText进行解析
     *
     * @param cyouHtmlParseRuleItem
     * @param ruleConfig
     * @param htmlText
     * @return
     * @throws ParserException
     */
    protected static String parseItemHtml(CyouHtmlParseRuleItem cyouHtmlParseRuleItem, RuleConfig ruleConfig, String htmlText) throws ParserException, ParseException {
        return parseItemHtml(ruleConfig, htmlText, cyouHtmlParseRuleItem.getEncoding(), cyouHtmlParseRuleItem.getPlainTextFormat());
    }

    /**
     * 对cyouHtmlParseRuleItem通过读取ruleConfig配置（nodeFilter，index等）对当前htmlText进行解析
     *
     * @param ruleConfig
     * @param htmlText
     * @param encoding
     * @param plainTextFormat
     * @return
     * @throws ParserException
     */
    protected static String parseItemHtml(RuleConfig ruleConfig, String htmlText, String encoding, String plainTextFormat) throws ParserException, ParseException {

        Parser parser = new Parser();
        parser.setInputHTML(htmlText);
        parser.setEncoding(encoding);

        NodeList nodeList = parser.parse(ruleConfig.getNodeFilter());
        if (nodeList == null || nodeList.size() == 0) {
            return "";
        }
        if (ruleConfig.getChild() != null) {
            return parseItemHtml(ruleConfig.getChild(), nodeList.elementAt(ruleConfig.getIndex()).toHtml().trim(), encoding, plainTextFormat);
        } else {
            if(ruleConfig.getIndex()==-1){
                StringBuffer sb = new StringBuffer();
                //sb.append("[");
                for(int i=0; i<nodeList.size(); i++){
                    String nodeValue = getNodeValue(ruleConfig, nodeList.elementAt(i));
                    sb.append(parsePlainText(plainTextFormat, nodeValue));
                    if(i<nodeList.size()-1){
                        sb.append(",");
                    }
                }
                //sb.append("]");
                return sb.toString();
            }
            return parsePlainText(plainTextFormat, nodeList.elementAt(ruleConfig.getIndex()).toPlainTextString().trim());
        }
    }

    private static String getNodeValue(RuleConfig ruleConfig, Node node){
        try {
            return (String)MethodUtils.invokeExactMethod(node, ruleConfig.getValueInvokeMethod(), new Object[]{});
        } catch (NoSuchMethodException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            return "";
        } catch (IllegalAccessException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            return "";
        } catch (InvocationTargetException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            return "";
        }
    }

    /**
     * 根据配置的format格式解析element值value
     *
     * @param plainTextFormat
     * @param value
     * @return
     */
    protected static String parsePlainText(String plainTextFormat, String value) {
        String plainTextType = plainTextFormat.split("[:]")[0];
        String[] args = (plainTextFormat.contains(":") && !plainTextFormat.endsWith(":")) ?
                plainTextFormat.split("[:]")[1].split(",") : new String[0];
        return PlainTextProcessUtil.process(plainTextType, value, args);
    }


}
